// Copyright 2022 jeff.li. and/or its affiliates.

#pragma once

#include <tbir/runtime/hash/hash.h>

#include <cstddef>
#include <cstdint>
#include <functional>
#include <utility>

#include <tbir/runtime/py_commons/pyhash.h>

namespace tbir::runtime {

    /*!
     * \brief Hash the binary bytes
     * \param bytes The data pointer
     * \param size The size of the bytes.
     * \return the hash value.
     */
    inline size_t BytesHash(const void *bytes, size_t size) noexcept {
        return Hasher::Hash(reinterpret_cast<const unsigned char *>(bytes), size);
    }

    template<class T, size_t = sizeof(T) / sizeof(size_t)>
    struct ScalarHash;

    template<class T>
    struct ScalarHash<T, 0> : public std::unary_function<T, size_t> {
        size_t operator()(T v) const noexcept {
            union {
                T t;
                size_t a;
            } u;
            u.a = 0;
            u.t = v;
            if (u.a == (size_t) -1) {
                u.a = (size_t) -2;
            }
            return u.a;
        }
    };

    template<class T>
    struct ScalarHash<T, 1> : public std::unary_function<T, size_t> {
        size_t operator()(T v) const noexcept {
            union {
                T t;
                size_t a;
            } u;
            u.t = v;
            if (u.a == (size_t) -1) {
                u.a = (size_t) -2;
            }
            return u.a;
        }
    };

    template<class T>
    struct ScalarHash<T, 2> : public std::unary_function<T, size_t> {
        size_t operator()(T v) const noexcept {
            union {
                T t;
                struct {
                    size_t a;
                    size_t b;
                } s;
            } u;
            u.t = v;
            return BytesHash(&u, sizeof(u));
        }
    };

    template<class T>
    struct ScalarHash<T, 3> : public std::unary_function<T, size_t> {
        size_t operator()(T v) const noexcept {
            union {
                T t;
                struct {
                    size_t a;
                    size_t b;
                    size_t c;
                } s;
            } u;
            u.t = v;
            return BytesHash(&u, sizeof(u));
        }
    };

    template<class T>
    struct ScalarHash<T, 4> : public std::unary_function<T, size_t> {
        size_t operator()(T v) const noexcept {
            union {
                T t;
                struct {
                    size_t a;
                    size_t b;
                    size_t c;
                    size_t d;
                } s;
            } u;
            u.t = v;
            return BytesHash(&u, sizeof(u));
        }
    };

    template<>
    struct ScalarHash<double, sizeof(double) / sizeof(size_t)> {
        size_t operator()(double v) const noexcept {
            return py_builtins::_Py_HashDouble(v);
        }
    };

    template<class T>
    struct ScalarHash<T *, sizeof(void *) / sizeof(size_t)> {
        size_t operator()(T *v) const noexcept {
            return py_builtins::_Py_HashPointer(v);
        }
    };

}  // namespace tbir::runtime
